package com.jacob.androidstudy.javanote;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;

/**
 * TCP和UDP都是传输层协议，区别：
 * 1.TCP是有链接的，发出数据包后需要对方响应。
 * UDP是无连接的，发送包后不需要对方响应。
 * 
 * 2.TCP可靠性好，也不会出现数据丢失。
 * UDP可靠性差，可能会出现数据丢失。即使出现数据丢失也不重发。
 * 
 * 3.UDP发送内容长度有限。
 * 
 * 3.大部分应用都应使用TCP协议。
 * 个别对数据丢失不敏感的应用可以使用UDP协议，例如语音视频聊天等。
 * 
 */
public class $19_TCP和UDP {

	/**
	 * TCP服务器端
	 */
	public void TCPServer(String[] args) throws IOException{
//		1.新建一个服务器端的ServerSocket。
		ServerSocket ss = new ServerSocket(54321); //此时ServerSocket对象绑定本机的54321端口。
		
//		2.建立和客户端的Socket连接。连接建立成功之间，程序会一直等待。同一个端口可以接受多个客户端连接，每个客户端连接时，都执行一下accept()方法即可。
		Socket s = ss.accept(); //ServerSocket.accept()方法会侦听绑定的端口，接受客户端的Socket连接请求，然后将Socket返回。此方法在连接传入之前一直阻塞。
		
//		3.通过Socket连接得到输入输出流。
		InputStream is = s.getInputStream(); //字节输入流，用来读客户端发送过来的数据。此对象的read()方法执行后，会阻塞并等待客户端write()数据。
		OutputStream os = s.getOutputStream(); //字节输出流，用来向客户端发送数据。此对象的write()方法执行后，会阻塞并等待客户端read()数据。
		
//		4.网络传输的一般都是字符，所以可以将输入输出流包装成字符流，再将字符流包装成缓冲流。
		BufferedReader br = new BufferedReader(new InputStreamReader(is,"UTF-8")); //用来读客户端发送过来的数据。
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os,"UTF-8")); //用来向客户端发送数据。
		
//		5.最后记住关闭流和连接。
		br.close();
		bw.close();
		s.close();
		ss.close();
		
	}
	
	
	public void TCPClient(String[] args) throws UnknownHostException, IOException {
//		1.建立和服务器端的Socket连接。连接建立成功后，服务器端的端口号既是54321，但客户端的端口号是随机的。
		Socket s = new Socket("127.0.0.1", 54321);
		
//		2.通过Socket连接得到输入输出流。
		InputStream is = s.getInputStream(); //字节输入流，用来读服务器端发送过来的数据。此对象的read()方法执行后，会阻塞并等待服务器端write()数据。
		OutputStream os = s.getOutputStream(); //字节输出流，用来向服务器端发送数据。此对象的write()方法执行后，会阻塞并等待服务器端read()数据。
		
//		4.网络传输的一般都是字符，所以可以将输入输出流包装成字符流，再将字符流包装成缓冲流。
		BufferedReader br = new BufferedReader(new InputStreamReader(is,"UTF-8")); //用来读服务器端发送过来的数据。
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os,"UTF-8")); //用来向服务器端发送数据。
		
//		5.最后记住关闭流和连接。
		br.close();
		bw.close();
		s.close();
		
	}
	
	
	public void UDPSend(String[] args) throws IOException {
//		1.定义用于存放待发送的byte数组。
		byte[] b="发送内容".getBytes();
		
//		2.byte数组不能直接存放发送数据，要用DatagramPacket包起来。
		DatagramPacket dp=new DatagramPacket(b,b.length,InetAddress.getLocalHost(),2345);
		
//		3.定义DatagramSocket监听指定端口。此端口会用来发送数据。
		DatagramSocket ds=new DatagramSocket(2345);
		
//		4.向指定地址的指定端口号发送数据。
		ds.send(dp);
		
//		5.一些有用的方法。
		dp.getLength(); //返回发送数据的长度。
		dp.getAddress(); //返回数据接收方的IP地址。
		dp.getPort(); //返回数据接收方的端口号。
		
//		5.最后记住关闭DatagramSocket。
		ds.close();
		
	}
	public void UDPReceive(String[] args) throws IOException {
//		1.定义用于存放接收数据的byte数组。接收的数据量不能超过byte数组的长度。
		byte[] b = new byte[2048];
		
//		2.byte数组不能直接存放接收数据，要用DatagramPacket包起来。
		DatagramPacket dp=new DatagramPacket(b,b.length); //构造 DatagramPacket，用来接收长度为 b.length 的数据包。
		
//		3.定义DatagramSocket监听指定端口。此端口会用来接收数据。
		DatagramSocket ds=new DatagramSocket(2345);
		
//		4.接收别的程序向指定端口发送的数据，并将数据放入DatagramPacket。
		ds.receive(dp); //此方法执行后，会进入阻塞状态，直到接收到数据。
		
//		5.一些有用的方法。
		dp.getLength(); //返回接收到的数据的长度。
		dp.getAddress(); //返回数据包发送方的IP地址。
		dp.getPort(); //返回数据包发送方的端口号。
		
//		6.最后别忘了关闭DatagramSocket。
		ds.close();
	}

}
